using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.IO;
using Lizard.Remoting.TFAccess;
using Ltf.Remoting.Commands;
using Ltf.Remoting.TeamFoundation;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Ltf.Remoting.Server;
using Ltf.Remoting.TFAccess;

namespace Ltf.Remoting.Operations
{
	public class WorkspaceOperators
	{

		private Workspace workspace;
		private int getCount = 0;
		private IBusyMessageReceiver  busyMessageReceiver;
		private TFServerConnection connection;

		public WorkspaceOperators(TFServerConnection connection, Workspace workspace)
			: this (connection, workspace, null)
		{		}

		public WorkspaceOperators(TFServerConnection connection, Workspace workspace, IBusyMessageReceiver busyMessageReceiver)
		{
			this.connection = connection;
			this.workspace = workspace;
			this.busyMessageReceiver = busyMessageReceiver;
		}

		public IBusyMessageReceiver BusyMessageReceiver
		{
			get { return this.busyMessageReceiver; }
			set { this.busyMessageReceiver = value; }
		}

		public delegate GetStatus GetDelegate(string[] pathlist, VersionSpec versionSpec, RecursionType recursionType, bool overwrite, bool forceAll);


		public GetStatus Get(string[] pathlist, VersionSpec versionSpec, RecursionType recursionType, bool overwrite, bool forceAll)
		{
			workspace.VersionControlServer.Getting += new GettingEventHandler(VersionControlServer_Getting);
			GetOptions options = GetOptions.None;
			if (overwrite)
				options |= GetOptions.Overwrite;
			if (forceAll)
				options |= GetOptions.GetAll;
			
			GetStatus gs = workspace.Get(pathlist, versionSpec, recursionType, options);

			List<string> localPaths = new List<string>();
			foreach (string path in pathlist)
			{
				if (path.StartsWith("$"))
					localPaths.Add(workspace.GetLocalItemForServerItem(path));
				else
					localPaths.Add(path);
			}
			ServerFunctions sf = new ServerFunctions();
			sf.StatusScanEx(localPaths.ToArray(), false);

			return gs;
		}

		public GetStatus Get(ItemSpec[] itemspecs, VersionSpec versionSpec, bool overwrite, bool forceAll)
		{
			List<GetRequest> requestsList = new List<GetRequest>();
			foreach(ItemSpec itemSpec in itemspecs)
				requestsList.Add(new GetRequest(itemSpec, versionSpec));
			GetRequest[] requests = requestsList.ToArray();

			workspace.VersionControlServer.Getting += new GettingEventHandler(VersionControlServer_Getting);
			GetOptions options = GetOptions.None;
			if (overwrite)
				options |= GetOptions.Overwrite;
			if (forceAll)
				options |= GetOptions.GetAll;
			
			GetStatus gs = workspace.Get(requests, options);

			List<string> localPaths = new List<string>();
			foreach (ItemSpec itemspec in itemspecs)
			{
				string path = itemspec.Item;
				if (path.StartsWith("$"))
					path=workspace.GetLocalItemForServerItem(path);

				if (!localPaths.Contains(Path.GetDirectoryName(path)))
					localPaths.Add(path);
			}
			ServerFunctions sf = new ServerFunctions();
			sf.StatusScanEx(localPaths.ToArray(), false);

			return gs;
		}


		public GetStatus GetWithForcedResolve(string path, VersionSpec versionSpec)
		{
			workspace.Undo(path);
			Conflict[] conflicts = workspace.QueryConflicts(new string[] { path }, false);
			Conflict[] resolvedConflicts;
			if (conflicts.Length > 0)
			{
				Item item = workspace.VersionControlServer.GetItem(workspace.GetServerItemForLocalItem(path));
				item.DownloadFile(path);
				workspace.ResolveConflict(conflicts[0]);
				workspace.RemoveLocalConflict(conflicts[0]);
			}
			
			
			GetStatus gs = workspace.Get(new string[] { path }, versionSpec, RecursionType.None, GetOptions.GetAll | GetOptions.Overwrite);




			return gs;
		}
		//public void GetFilterCallback(Workspace workspace, ILocalUpdateOperation[] operations, Object userData)
		//{
		//  string s = "l";
		//}

		void VersionControlServer_Getting(object sender, GettingEventArgs e)
		{
			if (busyMessageReceiver != null)
				busyMessageReceiver.SendMessage(string.Format("Getting {0}", e.SourceLocalItem));
			getCount++;
		}

		public void PendAdd(string[] files)
		{
			workspace.PendAdd(files, true);
		}

		public void PendEdit(string[] files)
		{
			workspace.PendEdit(files, RecursionType.Full);
		}

		public void PendEdit(string[] files, LockLevel lockLevel)
		{
			PendEdit(files, "Auto", lockLevel);
		}

		public void PendEdit(string[] files, string type, LockLevel lockLevel)
		{
			workspace.PendEdit(files, RecursionType.Full, type, lockLevel);
		}

		public void PendRename(string file, string newname)
		{
			string newpath = Path.Combine(Path.GetDirectoryName(file), newname);
			workspace.PendRename(file, newpath);
		}

		public void PendMove(string oldPath, string newPath)
		{
			workspace.PendRename(oldPath, newPath, LockLevel.Checkin, true, true);
		}

		public void PendDelete(string[] files)
		{
			workspace.PendDelete(files, RecursionType.Full, LockLevel.CheckOut);
		}

		public void UndoPending(PendingChange[] changes)
		{
			workspace.Undo(changes);
		}


		public delegate int CheckInDelegate(PendingChange[] changes, string comments, int[] workItems, bool resolveWorkItems);

		public int CheckIn(PendingChange[] changes, string comments, int[] workItems, bool resolveWorkItems)
		{
			WorkItemStore wis = new WorkItemStore(connection.TeamFoundationServer);
			List<WorkItemCheckinInfo> wicis = new List<WorkItemCheckinInfo>();
			foreach (int itemId in workItems)
			{
				WorkItemCheckinInfo wici = new WorkItemCheckinInfo(wis.GetWorkItem(itemId), resolveWorkItems ? WorkItemCheckinAction.Resolve : WorkItemCheckinAction.Associate);
				wicis.Add(wici);
			}
			return workspace.CheckIn(changes, comments, null, wicis.ToArray(), null);
		}

		public string Shelve(PendingChange[] changes, string name, string comments, int[] workItems, bool replaceExisting, bool revertChanges)
		{
			WorkItemStore wis = new WorkItemStore(connection.TeamFoundationServer);
			List<WorkItemCheckinInfo> wicis = new List<WorkItemCheckinInfo>();
			foreach (int itemId in workItems)
			{
				WorkItemCheckinInfo wici = new WorkItemCheckinInfo(wis.GetWorkItem(itemId), revertChanges ? WorkItemCheckinAction.Resolve : WorkItemCheckinAction.Associate);
				wicis.Add(wici);
			}
			string user = workspace.OwnerName;
			if (!string.IsNullOrEmpty(connection.Settings.User))
				user = connection.Settings.User;
			Shelveset shelveset = new Shelveset(workspace.VersionControlServer, name, user);
			shelveset.Comment = comments;
			shelveset.WorkItemInfo = wicis.ToArray();
			ShelvingOptions so = revertChanges ? ShelvingOptions.Move : ShelvingOptions.None;
			workspace.Shelve(shelveset, changes, so);
			return user;
		}



		public LizardTFItems GetTFItems(string[] pathList)
		{
			return GetTFItems(pathList, 0);
		}

		public LizardTFItems GetTFItems(string[] pathList, int version)
		{
			LizardTFItems tfItems = new LizardTFItems();

			List<ItemSpec> specs = new List<ItemSpec>();
			List<string> paths = new List<string>();
			foreach (string path in pathList)
			{
				VersionSpec versionSpec = VersionSpec.Latest;
				if (version != 0)
					versionSpec = new ChangesetVersionSpec(version);
				string serverItem = workspace.GetServerItemForLocalItem(path);
				GetRequest request = new GetRequest(serverItem, RecursionType.Full, versionSpec);
				GetStatus gs = workspace.Get(request, GetOptions.Preview);
				ItemSpec ispec = new ItemSpec(serverItem, RecursionType.Full);
				specs.Add(ispec);
				paths.Add(path);
			}


			ExtendedItem[][] extItemArrays = workspace.GetExtendedItems(specs.ToArray(), DeletedState.NonDeleted, ItemType.Any);

			List<string> outOfDateItems = new List<string>();
			foreach (ExtendedItem[] extItems in extItemArrays)
			{
				tfItems.AddRange(extItems);
			}

			string[] pathArray = paths.ToArray();

			Conflict[] conflicts = workspace.QueryConflicts(pathArray, true);
			tfItems.AddRange(conflicts);

			PendingChange[] pendingChanges = GetPendingChanges(pathArray, RecursionType.Full, true);
			tfItems.AddRange(pendingChanges);

			return tfItems;
		}

		private PendingChange[] GetPendingChanges(string[] pathArray, RecursionType recursionType, bool includeDownloadInfo)
		{
			PendingChange[] pendingChanges = workspace.GetPendingChanges(pathArray, recursionType, includeDownloadInfo);
			return pendingChanges;
		}

		public Conflict[] GetConflicts(string[] pathList)
		{
			List<string> paths = new List<string>();
			foreach (string path in pathList)
			{
				paths.Add(path);
			}

			string[] pathArray = paths.ToArray();

			return workspace.QueryConflicts(pathArray, true);

		}


		public void ParseSingleFile(string path)
		{
			try
			{
				ExtendedItem extItem = workspace.GetExtendedItems(new ItemSpec[] { new ItemSpec(path, RecursionType.None) }, DeletedState.NonDeleted, ItemType.Any)[0][0];
				GenerateOverlayInfo goi = new GenerateOverlayInfo(connection);
				goi.GenerateFileStatus(0, extItem, null, true, null, false);
			}
			catch
			{

			}
		}

		private void LoadTFConflicts(LizardTFItems tfItems, string[] pathList)
		{

		}

		public void GetVersionInfo(string path, out string localPath, out string serverPath, out int localVersion, out int serverVersion)
		{
			int v = 0;
			int d = 0;
			string name;
			LizardFileAccess.GetNameAndVersionFromPath(path, out name, out v, out d);
			VersionSpec vs = ChangesetVersionSpec.Latest;
			if (v > 0)
				vs = new ChangesetVersionSpec(v);

			serverVersion = localVersion = 0;
			serverPath = localPath = string.Empty;
			Item item = null;
			try
			{
				item = workspace.VersionControlServer.GetItem(name,vs,d);
			}
			catch { }

			if (item != null)
			{
				serverPath = item.ServerItem;
				if (item.ItemType == ItemType.File)
					serverVersion = item.ChangesetId;
				else
				{
					TFHistory history = new TFHistory(connection, path, workspace);
					serverVersion = history.GetServerLatest(vs);
				}
			}


			// ExtendedItem[] extItems = server.GetExtendedItems(path, DeletedState.Any, ItemType.Any);
			ExtendedItem[] extItems = workspace.GetExtendedItems
				(new ItemSpec[] { new ItemSpec(path, RecursionType.None) }, DeletedState.Any, ItemType.Any)[0];
			if (extItems.Length > 0)
			{
				ExtendedItem extItem = extItems[0];
				localVersion = extItem.VersionLocal;
				serverVersion = extItem.VersionLatest;
				localPath = extItem.LocalItem;
			}
		}

		public void Branch(string sourcePathAndVersion, string targetPath, bool updateDisk)
		{
			string sourcePath;
			VersionSpec versionSpec;
			LizardFileAccess.GetNameAndVersionSpecFromPath(sourcePathAndVersion, out sourcePath, out versionSpec);

			workspace.PendBranch(sourcePath, targetPath, versionSpec, LockLevel.None, updateDisk);

		}

		public PendingChange[] GetPendingChanges(string path, RecursionType recursionType)
		{
			return workspace.GetPendingChanges(path, recursionType);
		}

		public void CreateMapping(string serverItem, string localItem)
		{
			workspace.CreateMapping(new WorkingFolder(serverItem, localItem));
		}

		public void PendUndelete(string path, int deleteionId)
		{
			workspace.PendUndelete(path, deleteionId);
		}

		public string[][] GetRenames(string serverPath, int versionFrom, int versionTo, bool includePends)
		{
			return GetChangedItems(serverPath, versionFrom, versionTo, includePends, true);
		}

		public string[][] GetChangedItems(string serverPath, int versionFrom, int versionTo, bool includePends, bool renamesOnly)
		{

			Dictionary<string, string> renames = new Dictionary<string, string>();

			int vTo = versionTo;
			int vFrom = versionFrom;
			bool isReveresed = false;

			if (vTo < vFrom && vFrom != 0 && vTo !=0)
			{
				vTo = versionFrom;
				vFrom = versionTo;
				isReveresed = true;
			}

			List<LizardChangeset> lcss = ItemHistory.GetHistory(serverPath, false, vFrom, vTo, Math.Abs(vFrom - vTo)+1, RecursionType.Full, workspace.VersionControlServer);

			foreach (LizardChangeset lcs in lcss)
			{
				foreach (LizardChange c in lcs.Changes)
				{
					
					if (!renamesOnly || c.IsRename)
					{
						string oldname = workspace.VersionControlServer.GetItem(c.ItemId, c.ChangesetId - 1).ServerItem;
						string newname = c.ServerItem;

						if (renames.ContainsKey(oldname))
						{
							// must have been renamed more than once
							string origname = renames[oldname];
							renames.Remove(oldname);
							if (newname!=origname)  // don't record if renamed back to the original name again
								renames.Add(newname, origname);
						}
						else
							renames.Add(newname, oldname);
					}

				}
			}

			if (includePends)
			{
				// if 'includepends' then we're comparing to local files
				// so update all the newnames to be the local names.
				foreach (string key in new List<string>(renames.Keys)) // list beacuse enumerator over keys breaks if dictionary modified (even just changing a value)
				{
					string v = renames[key];
					if (key.StartsWith("$"))
					{
						string localname = workspace.TryGetLocalItemForServerItem(key);
						renames.Remove(key);
						renames.Add(localname, v);
					}
				}
				PendingChange[] pendingChanges = GetPendingChanges(serverPath, RecursionType.Full);
				foreach (PendingChange pc in pendingChanges)
				{
					if (!renamesOnly || (pc.ChangeType & ChangeType.Rename) == ChangeType.Rename)
					{
						string oldname = pc.SourceServerItem;
						string newname = pc.LocalItem;
						if (renames.ContainsKey(oldname))
						{
							// must have been renamed more than once
							string origname = renames[oldname];
							renames.Remove(oldname);
							if (newname != origname)  // don't record if renamed back to the original name again
								renames.Add(newname, origname);
						}
						else
							renames.Add(newname, oldname);
					}
				}
			}

			if (!isReveresed)
				return new string[][] { (new List<string>(renames.Keys)).ToArray(), (new List<string>(renames.Values)).ToArray() };
			else
				return new string[][] { (new List<string>(renames.Values)).ToArray(), (new List<string>(renames.Keys)).ToArray() };
		}

		public string[] GetMergeTargetPaths(string sourcePath)
		{
			List<string> mergeTargets = new List<string>();
			ItemSpec itemSpec = new ItemSpec(sourcePath, RecursionType.None);
			BranchHistoryTreeItem[] bhtis = workspace.VersionControlServer.GetBranchHistory(new ItemSpec[] { itemSpec }, VersionSpec.Latest)[0];
			foreach (BranchHistoryTreeItem bhti in bhtis[0].Children)
			{
				if (bhti.Relative.BranchToItem!=null)
					mergeTargets.Add(bhti.Relative.BranchToItem.ServerItem);
				if (bhti.Relative.BranchFromItem != null)
					mergeTargets.Add(bhti.Relative.BranchFromItem.ServerItem);
			}
			return mergeTargets.ToArray();
		}

		public Changeset[] GetMergeCandidates(string sourcePath, string targetPath)
		{
			List<Changeset> candidateChangeSets = new List<Changeset>();
			MergeCandidate[] candidates = workspace.VersionControlServer.GetMergeCandidates(sourcePath, targetPath,RecursionType.Full);
			foreach (MergeCandidate mc in candidates)
			{
				candidateChangeSets.Add(mc.Changeset);
			}
			return candidateChangeSets.ToArray();
		}
	}
}
